package P2PConnection;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

/**
 * Created with IntelliJ IDEA.
 * Authors: Henrique Alberto Rusa and Danilo Barbosa Porto
 * Date: 04/11/13
 *
 * This java class presents the FTP (File Transfer Protocol) server side of the application.
 * Here we will be using a singleton implementation because our server will listen on one connection port, creating a
 * ClientHandler (Nested Class) to each connection accepted by our application.
 */

public class FTPServer {

    private final int portNumber = 8756;

    //Socket to connect with the handled client over FTP
    private ServerSocket serverSocket;

    /*
     *  List of all the Download listeners added by the method addListener()
     */
    private ArrayList<DownloadListener> listeners;

    /*
     * This will be a private static because we utilize a singleton implementation where no one can
     * access through the class the instance.
     */
    private static FTPServer instance;

    /*
     * We will allocate the instance parameters in here, initializing it and we will put the constructor on private so
     * the user has to call the getInstance method to return the allocated object.
     */
    private FTPServer() {
        listeners = new ArrayList<DownloadListener>();
    }

    //Method getInstance that returns the instance of the FTPServer object
    public static synchronized FTPServer getInstance() {
        if(instance == null){
            instance = new FTPServer();
        }
        return instance;
    }

    /*
     *  Method that adds listeners to the terminal, where the list will be used to warn all the listening classes
     *  when an event occurs.
     */
    public void addListener(DownloadListener onAdd){
        this.listeners.add(onAdd);
    }

    /*
     *  Method that will get all the listeners of the class and warn them of a event.
     *  The File will be added when the call is done to the method.
     */
    public synchronized void newFileDownloaded(File evt){
        for(DownloadListener element : listeners){
            element.finishedDownload(evt);
        }
    }

    /*
 *  Method to start the Server.
 *  This will put the socket listening on the portNumber indicated on the definition above.
 *  First, it will allocate a server and associate it to the portNumber. Then keep the instance listening on the
 *  port with the handleClientRequest, and lastly it will close the connection given the status change of the
 *  boolean of control.
 */
    public void startServer() {
        try {
            serverSocket = new ServerSocket(portNumber);

            while (true){
                this.handleClientRequest();
            }

        } catch (IOException e) {
            // TODO
            // Put an log error server file (date, time and error)
        }
    }

    /*
     *  Method to handle the connection accepted by the server to the SocketClientHandler.
     *  It will just instantiate a new object to manage the request and if it does not work, will write a log error
     *  to the file error log.
     */
    private void handleClientRequest() {
        try {
            new FTPClientHandler(serverSocket.accept()).run();
        } catch (IOException e) {
            // TODO
            // put on log error (date, time, error)
        }
    }

    /*
     *  Nested class that handles the connections made to the FTPServer.
     *  This object will be instantiated and initialized by the FTPServer only when it gets a request for connection,
     *  just to put the server listening to different requests over the port.
     */
    private class FTPClientHandler implements Runnable{


        //Socket to connect with the handled client over FTP
        Socket clientSocket;

        // Parameter that reads data over the FTP connection
        DataInputStream dataInput;

        // Parameter thar writes data over the FTP connection
        DataOutputStream dataOutput;

        /*
         * Constructor of the class.
         * This method enables the input and output streams of data and make the thread starts itself to receive
         * a file over the FTP connection.
         * The connection parameter is the socket where the FTPServer are receiving a request of connection.
         */
        public FTPClientHandler(Socket connection) throws IOException {
            clientSocket = connection;
            dataInput   = new DataInputStream(clientSocket.getInputStream());
            dataOutput  = new DataOutputStream(clientSocket.getOutputStream());
            this.run();
        }

        /*
         * receiveFile is a method that receives a file through the FTP connection established with the FTPServer.
         * This method will verify if the sender tried to send a file that does not exists.
         * On receive, will create the file and notify the sender with a "File Send Successfully"
         */
        private File receiveFile() throws IOException {

            // Verify if file in sender exists
            String filename = dataInput.readUTF();
            if(filename.compareTo("File Not Found") == 0){
                return null;
            }

            // Creates a file with the received name
            File arq = new File(filename);

            // Request file send to Client
            dataOutput.writeUTF("Send File");

            // Create a file writer, a string to receive the data over the socket and a integer to compare end of file.
            FileOutputStream fileOutput = new FileOutputStream(arq);
            String buffer;
            int charBuffer;

            // Loop receiving a char a char over the connection, verifying each time if it is an end of file character
            do{
                buffer = dataInput.readUTF();
                charBuffer = Integer.parseInt(buffer);

                if (charBuffer != -1){
                    fileOutput.write(charBuffer);
                }
            }while ( charBuffer != -1);

            fileOutput.close();
            dataOutput.writeUTF("File Send Successfully");
            return arq;
        }
        /*
         * Method run of the thread, executing the receive of files.
         * This will verify if the client is going to send a file or disconnect from the connection.
         */
        @Override
        public void run() {
            String command;
            try {
                // Command of send or disconnect from client
                command = dataInput.readUTF();

                // If the command is SEND, receive file, or else if command is DISCONNECT end connection and clientSocket
                if(command.compareTo("SEND") == 0){
                    newFileDownloaded(this.receiveFile());
                }
                else {
                    return;
                }
            } catch (IOException e) {
                //TODO
            }
        }
    }
}




































